import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Port } from '../../common/protocol';
import { BoardsConfig } from './boards-config';
import { ArduinoCommands } from '../arduino-commands';
import {
  BoardsServiceProvider,
  AvailableBoard,
} from './boards-service-provider';
import { nls } from '@theia/core/lib/common';

export interface BoardsDropDownListCoords {
  readonly top: number;
  readonly left: number;
  readonly width: number;
  readonly paddingTop: number;
}

export namespace BoardsDropDown {
  export interface Props {
    readonly coords: BoardsDropDownListCoords | 'hidden';
    readonly items: Array<AvailableBoard & { onClick: () => void; port: Port }>;
    readonly openBoardsConfig: () => void;
  }
}

export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
  protected dropdownElement: HTMLElement;

  constructor(props: BoardsDropDown.Props) {
    super(props);

    let list = document.getElementById('boards-dropdown-container');
    if (!list) {
      list = document.createElement('div');
      list.id = 'boards-dropdown-container';
      document.body.appendChild(list);
      this.dropdownElement = list;
    }
  }

  render(): React.ReactNode {
    return ReactDOM.createPortal(this.renderNode(), this.dropdownElement);
  }

  protected renderNode(): React.ReactNode {
    const { coords, items } = this.props;
    if (coords === 'hidden') {
      return '';
    }
    const footerLabel = nls.localize(
      'arduino/board/openBoardsConfig',
      'Select other board and port…'
    );
    return (
      <div
        className="arduino-boards-dropdown-list"
        style={{
          position: 'absolute',
          ...coords,
        }}
      >
        {items
          .map(({ name, port, selected, onClick }) => ({
            label: nls.localize(
              'arduino/board/boardListItem',
              '{0} at {1}',
              name,
              Port.toString(port)
            ),
            selected,
            onClick,
          }))
          .map(this.renderItem)}
        <div
          key={footerLabel}
          className="arduino-boards-dropdown-item arduino-board-dropdown-footer"
          onClick={() => this.props.openBoardsConfig()}
        >
          <div>{footerLabel}</div>
        </div>
      </div>
    );
  }

  protected renderItem({
    label,
    selected,
    onClick,
  }: {
    label: string;
    selected?: boolean;
    onClick: () => void;
  }): React.ReactNode {
    return (
      <div
        key={label}
        className={`arduino-boards-dropdown-item ${selected ? 'selected' : ''}`}
        onClick={onClick}
      >
        <div>{label}</div>
        {selected ? <span className="fa fa-check" /> : ''}
      </div>
    );
  }
}

export class BoardsToolBarItem extends React.Component<
  BoardsToolBarItem.Props,
  BoardsToolBarItem.State
> {
  static TOOLBAR_ID: 'boards-toolbar';

  protected readonly toDispose: DisposableCollection =
    new DisposableCollection();

  constructor(props: BoardsToolBarItem.Props) {
    super(props);

    const { availableBoards } = props.boardsServiceClient;
    this.state = {
      availableBoards,
      coords: 'hidden',
    };

    document.addEventListener('click', () => {
      this.setState({ coords: 'hidden' });
    });
  }

  componentDidMount() {
    this.props.boardsServiceClient.onAvailableBoardsChanged((availableBoards) =>
      this.setState({ availableBoards })
    );
  }

  componentWillUnmount(): void {
    this.toDispose.dispose();
  }

  protected readonly show = (event: React.MouseEvent<HTMLElement>) => {
    const { currentTarget: element } = event;
    if (element instanceof HTMLElement) {
      if (this.state.coords === 'hidden') {
        const rect = element.getBoundingClientRect();
        this.setState({
          coords: {
            top: rect.top,
            left: rect.left,
            width: rect.width,
            paddingTop: rect.height,
          },
        });
      } else {
        this.setState({ coords: 'hidden' });
      }
    }
    event.stopPropagation();
    event.nativeEvent.stopImmediatePropagation();
  };

  render(): React.ReactNode {
    const { coords, availableBoards } = this.state;
    const boardsConfig = this.props.boardsServiceClient.boardsConfig;
    const title = BoardsConfig.Config.toString(boardsConfig, {
      default: nls.localize(
        'arduino/common/noBoardSelected',
        'No board selected'
      ),
    });
    const decorator = (() => {
      const selectedBoard = availableBoards.find(({ selected }) => selected);
      if (!selectedBoard || !selectedBoard.port) {
        return 'fa fa-times notAttached';
      }
      if (selectedBoard.state === AvailableBoard.State.guessed) {
        return 'fa fa-exclamation-triangle guessed';
      }
      return '';
    })();

    return (
      <React.Fragment>
        <div className="arduino-boards-toolbar-item-container">
          <div className="arduino-boards-toolbar-item" title={title}>
            <div className="inner-container" onClick={this.show}>
              <span className={decorator} />
              <div className="label noWrapInfo">
                <div className="noWrapInfo noselect">{title}</div>
              </div>
              <span className="fa fa-caret-down caret" />
            </div>
          </div>
        </div>
        <BoardsDropDown
          coords={coords}
          items={availableBoards
            .filter(AvailableBoard.hasPort)
            .map((board) => ({
              ...board,
              onClick: () => {
                if (board.state === AvailableBoard.State.incomplete) {
                  this.props.boardsServiceClient.boardsConfig = {
                    selectedPort: board.port,
                  };
                  this.openDialog();
                } else {
                  this.props.boardsServiceClient.boardsConfig = {
                    selectedBoard: board,
                    selectedPort: board.port,
                  };
                }
              },
            }))}
          openBoardsConfig={this.openDialog}
        ></BoardsDropDown>
      </React.Fragment>
    );
  }

  protected openDialog = () => {
    this.props.commands.executeCommand(ArduinoCommands.OPEN_BOARDS_DIALOG.id);
    this.setState({ coords: 'hidden' });
  };
}
export namespace BoardsToolBarItem {
  export interface Props {
    readonly boardsServiceClient: BoardsServiceProvider;
    readonly commands: CommandRegistry;
  }

  export interface State {
    availableBoards: AvailableBoard[];
    coords: BoardsDropDownListCoords | 'hidden';
  }
}
